package base

import (
	"strconv"
	"strings"
	"time"

	"gitee.com/xuesongtao/gotool/internal"
)

const (
	DayInt                       = 24 * 3600
	DayDur                       = 24 * time.Hour
	StartTimeSuffix              = " 00:00:00"
	EndTimeSuffix                = " 23:59:59"
	GetDateBetweenForDate  int32 = 1 // 按日处理
	GetDateBetweenForMonth int32 = 2 // 按月处理
	GetDateBetweenForYear  int32 = 3 // 按年处理
)

const (
	// 时间格式
	YearFmt int8 = 1 << iota
	MonthFmt
	DayFmt
	HourFmt
	MinFmt
	SecFmt
	MilliFmt // 毫秒

	DateFmtFlag          = YearFmt | MonthFmt | DayFmt
	DateTimeFmtFlag      = DateFmtFlag | HourFmt | MinFmt | SecFmt
	MilliDateTimeFmtFlag = DateFmtFlag | HourFmt | MinFmt | SecFmt | MilliFmt
)

var (
	DateFmt     = GetTimeFmt(DateFmtFlag)     // 2006-01-02
	DatetimeFmt = GetTimeFmt(DateTimeFmtFlag) // 2006-01-02 15:04:05
)

type weekInfo struct {
	StartTimeObj time.Time         // 本周的最开始对象
	EndTimeObj   time.Time         // 本周的结束对象
	StartDate    string            // 本周开始日期
	EndDate      string            // 本周结束日期
	Dates        []string          // 日期
	Date2WeekMap map[string]string // 保存的日期对应的星期几, map[2021-09-01]星期一
}

// FormatBusinessDate 将输入的时间转为业务需要时间, 规则如下:
// 1. 1分钟内发布的: 刚刚
// 2. 1小时内发布的: X分钟前
// 3. 超过1小时, 仍在当天: xx:xx
// 4. 跨天，但少于24小时: 昨天 xx:xx
// 5. 跨天, 超过24小时: xxxx-xx-xx xx:xx
func FormatBusinessDate(date string) (finalStr string) {
	timeObj := Datetime2TimeObj(date, DatetimeFmt)
	inDate := timeObj.Format(DateFmt) // 输入的日期
	inTimestamp := timeObj.Unix()     // 输入的时间戳

	curTimeObj := time.Now()
	curDate := curTimeObj.Format(DateFmt)
	curTimestamp := curTimeObj.Unix()

	var (
		minute int64 = 60 // 1 分钟
		hour         = 60 * minute
		day          = 24 * hour
	)
	diffVal := curTimestamp - inTimestamp
	if diffVal < minute {
		finalStr = "刚刚"
	} else if diffVal < hour {
		finalStr = ToString(diffVal/minute) + "分钟前"
	} else if diffVal > hour && inDate == curDate {
		// 2021-08-10 10:37:35 => 10:37
		finalStr = date[11:16]
	} else if inTimestamp > curTimestamp-day {
		finalStr = "昨天 " + date[11:16]
	} else {
		finalStr = date[:16]
	}
	return
}

// GetWeekInfoForTime 根据输入的 time 来获取本周的日期和星期几, 如果没有输入默认按当前时间处理
func GetWeekInfoForTime(dateFormat string, t ...time.Time) *weekInfo {
	weeks := []string{"星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"}
	if dateFormat == "" {
		dateFormat = DateFmt
	}
	curTimeObj := time.Now()
	if len(t) > 0 {
		curTimeObj = t[0]
	}
	curWeekDayInt := int(curTimeObj.Weekday())
	if curWeekDayInt == 0 {
		curWeekDayInt = 7
	}
	startTimeObj := curTimeObj.AddDate(0, 0, 1-curWeekDayInt)
	endTimeObj := startTimeObj.AddDate(0, 0, 6)
	startDate := startTimeObj.Format(dateFormat)
	endDate := endTimeObj.Format(dateFormat)
	date2weekMap := make(map[string]string, 7)
	dates := make([]string, 7)
	for i := 0; i <= 6; i++ {
		tmpDate := startTimeObj.AddDate(0, 0, i).Format(dateFormat)
		dates[i] = tmpDate
		date2weekMap[tmpDate] = weeks[i]
	}
	return &weekInfo{
		StartTimeObj: startTimeObj,
		EndTimeObj:   endTimeObj,
		StartDate:    startDate,
		EndDate:      endDate,
		Dates:        dates,
		Date2WeekMap: date2weekMap,
	}
}

// DatetimeIsExpire 判断输入时间是否过期
func DatetimeIsExpire(date string, expire ...time.Duration) bool {
	timeObj, err := time.ParseInLocation(DatetimeFmt, date, time.Local)
	if err != nil { // 按过期处理
		internal.Error("time.ParseInLocation is failed, err: ", err)
		return true
	}

	if len(expire) > 0 {
		return timeObj.Add(expire[0]).Before(time.Now())
	}
	return timeObj.Before(time.Now())
}

// SubDate 获取两个时间的差值
func SubDate(date, sub string, format ...string) time.Duration {
	defaultFormat := DateFmt
	if len(format) > 0 {
		defaultFormat = format[0]
	}
	dateObj, err := time.ParseInLocation(defaultFormat, date, time.Local)
	if err != nil {
		internal.Error("time.ParseInLocation is failed, err: ", err)
		return 0
	}
	subObj, err := time.ParseInLocation(defaultFormat, sub, time.Local)
	if err != nil {
		internal.Error("time.ParseInLocation is failed, err: ", err)
		return 0
	}
	return dateObj.Sub(subObj)
}

// AddDate 增加时间
func AddDate(date string, add time.Duration, format ...string) time.Time {
	defaultFormat := DateFmt
	if len(format) > 0 {
		defaultFormat = format[0]
	}
	return Datetime2TimeObj(date, defaultFormat).Add(add)
}

// AddDateForInt 增加时间
func AddDateForInt(date string, years, months, days int, format ...string) time.Time {
	defaultFormat := DateFmt
	if len(format) > 0 {
		defaultFormat = format[0]
	}
	return Datetime2TimeObj(date, defaultFormat).AddDate(years, months, days)
}

// Datetime2Timestamp 将时间字符串转为时间戳
func Datetime2Timestamp(date string, format ...string) int64 {
	defaultFormat := DatetimeFmt
	if len(format) > 0 {
		defaultFormat = format[0]
	}
	return Datetime2TimeObj(date, defaultFormat).Unix()
}

// Timestamp2Datetime 将时间戳转为时间字符串
func Timestamp2Datetime(timestamp int64, format ...string) string {
	defaultFormat := DatetimeFmt
	if len(format) > 0 {
		defaultFormat = format[0]
	}
	return time.Unix(timestamp, 0).Format(defaultFormat)
}

// GetAgeForBirthday 根据出生日期获取年龄
func GetAgeForBirthday(birthday string, format ...string) (age int) {
	if birthday == "" || len(birthday) < 4 {
		return
	}

	defaultFormat := DateFmt
	if len(format) > 0 && format[0] != "" {
		defaultFormat = format[0]
	}

	// 计算 age
	subStr := time.Since(Datetime2TimeObj(birthday, defaultFormat)).String()
	hourIndex := strings.Index(subStr, "h")
	if hourIndex <= 0 {
		return
	}

	hourInt, _ := strconv.Atoi(subStr[:hourIndex])
	return hourInt / 24 / 365
}

// SubNow2Year 目标年与现在的差
func SubNow2Year(year string) string {
	yearInt, _ := strconv.Atoi(year)
	nowInt := time.Now().Year()
	sub := nowInt - yearInt
	if sub <= 0 {
		sub = 0
	}
	return ToString(sub)
}

// GetCurMonthDur 获取指定月的开始和结束时间
func GetTargeMonthDur(target time.Time) (startDatetime, endDatetime string) {
	if target.Day() == 1 { // 说明为 1 号, 可以直接处理
		startDatetime = target.Format(DateFmt) + StartTimeSuffix
		endDatetime = target.AddDate(0, 1, 0).AddDate(0, 0, -1).Format(DateFmt) + EndTimeSuffix
		return
	}

	startDatetime = target.Format(DateFmt)[:7] + "-01" + StartTimeSuffix
	endDatetime = AddDateForInt(startDatetime, 0, 1, 0, DatetimeFmt).AddDate(0, 0, -1).Format(DateFmt) + EndTimeSuffix
	return
}

// GetAddDateDur 获取指定时间的当天开始时间和结束时间
func GetTargetDateDur(target time.Time) (startDatetime, endDatetime string) {
	fmtDate := target.Format(DateFmt)
	startDatetime = fmtDate + StartTimeSuffix
	endDatetime = fmtDate + EndTimeSuffix
	return
}

// Datetime2TimeObj
func Datetime2TimeObj(target string, format ...string) time.Time {
	defaultFormat := DatetimeFmt
	if len(format) > 0 {
		defaultFormat = format[0]
	}
	timeObj, err := time.ParseInLocation(defaultFormat, target, time.Local)
	if err != nil {
		internal.Error("time.ParseInLocation is failed, err: ", err)
		return time.Time{}
	}
	return timeObj
}

// DatetimeBetween 判断目标时间是否在开始时间和结束时间之间
func DatetimeBetween(target, startDatetime, endDatetime string) bool {
	targetObj := Datetime2TimeObj(target)
	startObj := Datetime2TimeObj(startDatetime)
	endObj := Datetime2TimeObj(endDatetime)
	if startObj.Equal(targetObj) || endObj.Equal(targetObj) {
		return true
	}
	return startObj.Before(targetObj) && endObj.After(targetObj)
}

// GetDateBetween 获取开始时间和结束时间中的日期
// args[0] 为操作类型, 默认为 GetDateBetweenForDate
// args[1] 为了防止死循环设置的最大循环次数, 默认 365(1年)
func GetDateBetween(startDate, endDate string, args ...int32) []string {
	// internal.Infof("GetDateBetween s: %s, e: %s, args: %v", startDate, endDate, args)
	if len(startDate) != 10 {
		return nil
	}

	if len(startDate) != len(endDate) {
		return nil
	}

	startDateObj := Datetime2TimeObj(startDate, DateFmt)
	endDateObj := Datetime2TimeObj(endDate, DateFmt)
	if startDateObj.After(endDateObj) {
		return nil
	}
	if startDate == endDate {
		return []string{startDate}
	}

	defaultType := GetDateBetweenForDate
	addParams := []int{0, 0, 1}
	stop := 365
	if len(args) > 0 {
		defaultType = args[0]
		if len(args) == 2 {
			stop = int(args[1])
		}
	}
	if !InInt32Array(defaultType, GetDateBetweenForDate, GetDateBetweenForMonth, GetDateBetweenForYear) {
		return nil
	}

	if defaultType == GetDateBetweenForMonth {
		addParams = []int{0, 1, 0}
		startDate = startDate[:7]
		endDate = endDate[:7]
		if startDate == endDate {
			return []string{startDate}
		}
	}

	if defaultType == GetDateBetweenForYear {
		addParams = []int{1, 0, 0}
		startDate = startDate[:4]
		endDate = endDate[:4]
		if startDate == endDate {
			return []string{startDate}
		}
	}

	res := []string{startDate}
	for i := 0; i < stop; i++ {
		startDateObj = startDateObj.AddDate(addParams[0], addParams[1], addParams[2])
		tmpDateFmt := startDateObj.Format(DateFmt)
		if defaultType == GetDateBetweenForMonth {
			tmpDateFmt = tmpDateFmt[:7]
		}
		if defaultType == GetDateBetweenForYear {
			tmpDateFmt = tmpDateFmt[:4]
		}
		res = append(res, tmpDateFmt)
		if tmpDateFmt == endDate {
			break
		}
	}
	return res
}

// Second2Hour 秒转分
func Second2Hour(s int) (hour, min, sec int) {
	if s <= 0 {
		return
	}

	hour = s / 3600
	min = s / 60 % 60
	sec = s % 60
	return
}

// GetTimeFmt 获取时间格式化
// splits 为分隔符
// splits[0] 为 [年月日] 的分割符, 默认为 "-"
// splits[1] 为 [年月日] 和 [时分秒] 的分割符, 默认为 " "
// splits[2] 为 [时分秒] 的分割符, 默认为 ":"
// splits[3] 为 后缀 的分割符, 默认为 "", 如: xxxx-xx-xxTxx:xx:xxZ
func GetTimeFmt(fmtType int8, splits ...string) string {
	defaultDateSplit := "-"
	defaultDateTimeSplit := " "
	defaultTimeSplit := ":"
	defaultSuffix := ""
	l := len(splits)
	switch l {
	case 1:
		defaultDateSplit = splits[0]
	case 2:
		defaultDateSplit = splits[0]
		defaultDateTimeSplit = splits[1]
	case 3:
		defaultDateSplit = splits[0]
		defaultDateTimeSplit = splits[1]
		defaultTimeSplit = splits[2]
	case 4:
		defaultDateSplit = splits[0]
		defaultDateTimeSplit = splits[1]
		defaultTimeSplit = splits[2]
		defaultSuffix = splits[3]
	}

	joinFn := func(old, split, join string) string {
		if old == "" {
			return join
		}
		if join == "" {
			return old
		}
		return old + split + join
	}

	// 年月日
	prefix := ""
	if fmtType&YearFmt > 0 {
		prefix = joinFn(prefix, defaultDateSplit, "2006")
	}
	if fmtType&MonthFmt > 0 {
		prefix = joinFn(prefix, defaultDateSplit, "01")
	}
	if fmtType&DayFmt > 0 {
		prefix = joinFn(prefix, defaultDateSplit, "02")
	}

	// 时分秒
	suffix := ""
	if fmtType&HourFmt > 0 {
		suffix = joinFn(suffix, defaultTimeSplit, "15")
	}
	if fmtType&MinFmt > 0 {
		suffix = joinFn(suffix, defaultTimeSplit, "04")
	}
	if fmtType&SecFmt > 0 {
		suffix = joinFn(suffix, defaultTimeSplit, "05")
	}
	if fmtType&MilliFmt > 0 {
		suffix = joinFn(suffix, ".", "000")
	}
	return joinFn(prefix, defaultDateTimeSplit, suffix) + defaultSuffix
}
